
/*******************************************************************************
*  模块名称：《byC51》驱动库的软延时模块
*
*  模块说明：用于实现51单片机及其兼容机的精确软延时
*            1.支持1~12T的各型51单片机
*            2.支持0~50MHz的各种晶振频率
*            3.支持0~50s的绝对延时（零误差或几乎零误差）
*            4.支持0~4294967295个机器周期的相对延时时长（零误差）
*
*  源码地址：https://gitee.com/baiyu_2019/by-c51
*
*  创 作 者：丘志远
*
*  当前版本：V1.0.0
*
*  创作日期：2021年7月26日
*******************************************************************************/

#ifndef __DELAY_H__
#define __DELAY_H__


#include <intrins.h>
#include "type.h"



#define CRYSTAL  12.0       //当前单片机使用几MHz的晶振频率？（请用小数）
#define MCCYCLE  12         //当前单片机的每个机器周期包含几个时钟周期？

/*******************************************************************************
*  使用说明
*
*  1.首先根据实际情况，为上面的CRYSTAL和MCCYCLE宏设置正确的值
*    然后就可以使用本模块了
*
*  2.以下宏可实现绝对延时，建议用户使用
*    delay_us(u8/u32/float us)      零/低误差延时0~50000000us
*    delay_ms(u8/u32/float ms)      零/低误差延时0~50000ms
*    delay_s(u8/u32/float s)        零/低误差延时0~50s
*
*  3.以下宏实现的是相对延时，不鼓励用户使用
*    wait_ck(u8/u32 n)              零误差延时0~4294967295个机器周期
*
*  4.以下函数实现的也是相对延时，不鼓励用户使用
*    wait_u8(u8 n)                  延时 2*n+5 个时钟周期
*    wait_u16_ck(u16 n)             延时 8*n+6 个时钟周期
*    wait_u32_ck(u32 n)             延时 17*n+8 个时钟周期
*
*  5.宏的参数必须是数值字面量或数值字面量表达式，而不能是语句、变量或变量表达式
*    1)正确示例
*      #define T 300
*      delay_us(300);               300是数值字面量
*      delay_us(300*20);            300*20是数值字面量表达式
*      delay_us(T/20);              T/2是数值字面量表达式
*    2)错误示例
*      u8 t = 300;
*      delay_us(t);                 t是变量
*      delay_us(t*2);               t*2是变量表达式
*      delay_us(t++);               t++是语句+变量表达式
*
*  6.delay_us、delay_ms、delay_s均是基于零误差的wait_ck实现的宏，故其在理论上应
*  当是零误差的。但受限于float计算精度的影响，它们可能会存在1个机器周期的绝对误
*  差，或百万分之一的相对误差。
*
*******************************************************************************/

#pragma OPTIMIZE(0)

void wait_u8_ck(u8 n);              //延时 2*n+5 个时钟周期
void wait_u16_ck(u16 n);            //延时 8*n+6 个时钟周期
void wait_u32_ck(u32 n);            //延时 17*n+8 个时钟周期

#define _ck2u32(n)      ((u32)(((n) - ((n)%2 ? 34 : 17))/17))
#define _ck2u32u8(n)    ((u8)(((n)-13-_ck2u32(n)*17)/2))

#define _wait_ck(n)	\
    (n) <= 1 ? (_nop_(), 0)   \
  : (n) == 2 ? (_nop_(), _nop_(), 0)	\
  : (n) == 3 ? (_nop_(), _nop_(), _nop_(), 0)	\
  : (n) == 4 ? (_nop_(), _nop_(), _nop_(), _nop_(), 0)	\
  : (n) == 5 ? (_nop_(), _nop_(), _nop_(), _nop_(), _nop_(), 0)	\
  : (n) == 6 ? (_nop_(), _nop_(), _nop_(), _nop_(), _nop_(), _nop_(), 0)	\
  : (n) <= 515 ? (wait_u8_ck((((n)%2 == 0 ? (_nop_(), 0) : 0), (u8)(((n) - 5)/2))), 0)	\
  : (wait_u8_ck((wait_u32_ck(_ck2u32(n)), _ck2u32u8(n))), 0)	\

#define wait_ck(n)      _wait_ck((u32)(n))
#define delay_us(us)    wait_ck((CRYSTAL)/(MCCYCLE)*((us) >= 50000000 ? 50000000 : (us)))
#define delay_ms(ms)    wait_ck((CRYSTAL)/(MCCYCLE)*((ms) >= 50000 ? 50000 : (ms))*1000)
#define delay_s(s)      wait_ck((CRYSTAL)/(MCCYCLE)*((s) >= 50 ? 50 : (s))*1000000)



#endif